#ifndef CSVCERT_H
#define CSVCERT_H

#include <string.h>
#include <stdint.h>
#include <openssl/evp.h>
#include <openssl/sm2.h>
#include <openssl/sm3.h>
#include <openssl/sms4.h>

#define SM2_KEY_SIZE         32
#define SM2_PUBKEY_SIZE      64
#define SM2_SHAREKEY_SIZE    32
#define SM2_UID_SIZE         256
#define HASH_BLOCK_LEN       32

/**
 * CSV Error Codes (each entry stored in a byte).
 */
typedef enum __attribute__((mode(QI))) CSV_ERROR_CODE {
    STATUS_SUCCESS               = 0x00,
    ERROR_INVALID_PLATFORM_STATE = 0x01,
    ERROR_INVALID_GUEST_STATE    = 0x02,
    ERROR_INVALID_CONFIG         = 0x03,
    ERROR_INVALID_LENGTH         = 0x04,
    ERROR_ALREADY_OWNED          = 0x05,
    ERROR_INVALID_CERTIFICATE    = 0x06,
    ERROR_POLICY_FAILURE         = 0x07,
    ERROR_INACTIVE               = 0x08,
    ERROR_INVALID_ADDRESS        = 0x09,
    ERROR_BAD_SIGNATURE          = 0x0A,
    ERROR_BAD_MEASUREMENT        = 0x0B,
    ERROR_ASID_OWNED             = 0x0C,
    ERROR_INVALID_ASID           = 0x0D,
    ERROR_WBINVD_REQUIRED        = 0x0E,
    ERROR_DFFLUSH_REQUIRED       = 0x0F,
    ERROR_INVALID_GUEST          = 0x10,
    ERROR_INVALID_COMMAND        = 0x11,
    ERROR_ACTIVE                 = 0x12,
    ERROR_HWERROR_PLATFORM       = 0x13,
    ERROR_HWERROR_UNSAFE         = 0x14,
    ERROR_UNSUPPORTED            = 0x15,
    ERROR_INVALID_PARAM          = 0x16,
    ERROR_RESOURCE_LIMIT         = 0x17,
} CSV_ERROR_CODE;

#define CSV_RSA_PUB_KEY_MAX_BITS   4096
#define CSV_ECDSA_PUB_KEY_MAX_BITS 576
#define CSV_ECDH_PUB_KEY_MAX_BITS  576
#define CSV_PUB_KEY_SIZE           (CSV_RSA_PUB_KEY_MAX_BITS / 8)

/**
 * CSV RSA Public key information.
 *
 * @modulus_size - Size of modulus in bits.
 * @pub_exp      - The public exponent of the public key.
 * @modulus      - The modulus of the public key.
 */
typedef struct __attribute__((__packed__)) csv_rsa_pub_key_t {
    uint32_t modulus_size;
    uint8_t  pub_exp[CSV_RSA_PUB_KEY_MAX_BITS / 8];
    uint8_t  modulus[CSV_RSA_PUB_KEY_MAX_BITS / 8];
} csv_rsa_pub_key;

/**
 * CSV Elliptical Curve DSA algorithm details.
 *
 * @curve - The CSV Elliptical curve ID.
 * @qx    - x component of the public point Q.
 * @qy    - y component of the public point Q.
 * @rmbz  - RESERVED. Must be zero!
 */
typedef struct __attribute__((__packed__)) csv_ecdsa_pub_key_t {
    uint32_t curve;  // CSV_EC as a uint32_t
    uint8_t  qx[CSV_ECDSA_PUB_KEY_MAX_BITS / 8];
    uint8_t  qy[CSV_ECDSA_PUB_KEY_MAX_BITS / 8];
    uint8_t  rmbz[CSV_PUB_KEY_SIZE - 2 * CSV_ECDSA_PUB_KEY_MAX_BITS / 8 - sizeof(uint32_t)];
} csv_ecdsa_pub_key;

/**
 * CSV Elliptical Curve Diffie Hellman Public Key details.
 *
 * @curve - The CSV Elliptical curve ID.
 * @qx    - x component of the public point Q.
 * @qy    - y component of the public point Q.
 * @rmbz  - RESERVED. Must be zero!
 */
typedef struct __attribute__((__packed__)) csv_ecdh_pub_key_t {
    uint32_t curve;  // CSV_EC as a uint32_t
    uint8_t  qx[CSV_ECDH_PUB_KEY_MAX_BITS / 8];
    uint8_t  qy[CSV_ECDH_PUB_KEY_MAX_BITS / 8];
    uint8_t  rmbz[CSV_PUB_KEY_SIZE - 2 * CSV_ECDH_PUB_KEY_MAX_BITS / 8 - sizeof(uint32_t)];
} csv_ecdh_pub_key;

typedef enum _curve_id {
    CURVE_ID_TYPE_INVALID = 0,
    CURVE_ID_TYPE_MIN     = 0X1,
    CURVE_ID_TYPE_P256    = 0x1,
    CURVE_ID_TYPE_P384    = 0x2,
    CURVE_ID_TYPE_SM2_256 = 0x3,
    CURVE_ID_TYPE_MAX     = 0X3
} curve_id_t;

#define SM2_UID_SIZE_U 256

typedef struct _userid_u {
    unsigned short len;
    unsigned char  uid[SM2_UID_SIZE_U - sizeof(unsigned short)];
} userid_u;

#define CERT_ECC_MAX_KEY_SIZE      72
#define CERT_SM2_KEY_RESERVED_SIZE 624

typedef struct __attribute__((__packed__)) csv_sm2_pub_key_t {
    curve_id_t    curve;
    unsigned char qx[CERT_ECC_MAX_KEY_SIZE];
    unsigned char qy[CERT_ECC_MAX_KEY_SIZE];
    userid_u      userid;
    unsigned char reserved[CERT_SM2_KEY_RESERVED_SIZE];
} csv_sm2_pub_key;

/**
 * The CSV Public Key memory slot may hold RSA, ECDSA, or ECDH.
 */
typedef union {
    csv_rsa_pub_key   rsa;
    csv_ecdsa_pub_key ecdsa;
    csv_ecdh_pub_key  ecdh;
    csv_sm2_pub_key   sm2;
} csv_pubkey;

/**
 * CSV Signature may be RSA or ECDSA.
 */
#define CSV_RSA_SIG_MAX_BITS        4096
#define CSV_ECDSA_SIG_COMP_MAX_BITS 576
#define CSV_SIG_SIZE                (CSV_RSA_SIG_MAX_BITS / 8)

/**
 * CSV RSA Signature data.
 *
 * @S - Signature bits.
 */
typedef struct __attribute__((__packed__)) csv_rsa_sig_t {
    uint8_t s[CSV_RSA_SIG_MAX_BITS / 8];
} csv_rsa_sig;

/**
 * CSV Elliptical Curve Signature data.
 *
 * @r    - R component of the signature.
 * @s    - S component of the signature.
 * @rmbz - RESERVED. Must be zero!
 */
typedef struct __attribute__((__packed__)) csv_ecdsa_sig_t {
    uint8_t r[CSV_ECDSA_SIG_COMP_MAX_BITS / 8];
    uint8_t s[CSV_ECDSA_SIG_COMP_MAX_BITS / 8];
    uint8_t rmbz[CSV_SIG_SIZE - 2 * CSV_ECDSA_SIG_COMP_MAX_BITS / 8];
} csv_ecdsa_sig;

/**
 * CSV Signature may be RSA or ECDSA.
 */
typedef union {
    csv_rsa_sig   rsa;
    csv_ecdsa_sig ecdsa;
} csv_sig;

/**
 * CSV Usage codes.
 */
typedef enum __attribute__((mode(HI))) CSV_USAGE {
    CSV_USAGE_HRK     = 0x0,
    CSV_USAGE_HSK     = 0x13,
    CSV_USAGE_INVALID = 0x1000,
    CSV_USAGE_OCA     = 0x1001,
    CSV_USAGE_PEK     = 0x1002,
    CSV_USAGE_PDH     = 0x1003,
    CSV_USAGE_CEK     = 0x1004,
} CSV_USAGE;

/**
 * CSV Algorithm cipher codes.
 */
typedef enum __attribute__((mode(HI))) CSV_SIG_ALGO {
    CSV_SIG_ALGO_INVALID      = 0x0,
    CSV_SIG_ALGO_RSA_SHA256   = 0x1,
    CSV_SIG_ALGO_ECDSA_SHA256 = 0x2,
    CSV_SIG_ALGO_ECDH_SHA256  = 0x3,
    CSV_SIG_ALGO_TYPE_SM2_SA  = 0x4,
    CSV_SIG_ALGO_TYPE_SM2_DH  = 0x5,
    CSV_SIG_ALGO_RSA_SHA384   = 0x101,
    CSV_SIG_ALGO_ECDSA_SHA384 = 0x102,
    CSV_SIG_ALGO_ECDH_SHA384  = 0x103,
} CSV_SIG_ALGO;

#define CSV_CERT_MAX_VERSION    1  // Max supported version
#define CSV_CERT_MAX_SIGNATURES 2  // Max number of sig's

/**
 * CSV Certificate format.
 *
 * @version       - Certificate version, set to 01h.
 * @api_major     - If PEK, set to API major version, otherwise zero.
 * @api_minor     - If PEK, set to API minor version, otherwise zero.
 * @reserved_0    - RESERVED, Must be zero!
 * @reserved_1    - RESERVED, Must be zero!
 * @pub_key_usage - Public key usage              (CSV_SIG_USAGE).
 * @pub_key_algo  - Public key algorithm          (CSV_SIG_ALGO).
 * @pub_key       - Public Key.
 * @sig_1_usage   - Key usage of SIG1 signing key (CSV_SIG_USAGE).
 * @sig_1_algo    - First signature algorithm     (CSV_SIG_ALGO).
 * @sig_1         - First signature.
 * @sig_2_usage   - Key usage of SIG2 signing key (CSV_SIG_USAGE).
 * @sig_2_algo    - Second signature algorithm    (CSV_SIG_ALGO).
 * @sig_2         - Second signature
 */
typedef struct __attribute__ ((__packed__)) csv_cert_t {
    uint32_t   version;    // Certificate Version. Should be 1.
    uint8_t    api_major;  // Version of API generating the
    uint8_t    api_minor;  // certificate. Unused during validation.
    uint8_t    reserved_0;
    uint8_t    reserved_1;
    uint32_t   pub_key_usage;  // CSV_USAGE
    uint32_t   pub_key_algo;   // CSV_SIG_ALGO
    csv_pubkey pub_key;
    uint32_t   sig_1_usage;  // CSV_USAGE
    uint32_t   sig_1_algo;   // CSV_SIG_ALGO
    csv_sig    sig_1;
    uint32_t   sig_2_usage;  // CSV_USAGE
    uint32_t   sig_2_algo;   // CSV_SIG_ALGO
    csv_sig    sig_2;
} csv_cert;

typedef struct _ecc_pubkey {
    uint32_t curve_id;
    uint32_t Qx[72 / 4];
    uint32_t Qy[72 / 4];
    uint32_t user_id[256 / 4];
} ecc_pubkey_t;
typedef struct _ecc_signature {
    uint32_t sig_r[72 / 4];
    uint32_t sig_s[72 / 4];
} ecc_signature_t;

typedef struct _sm2_userid {
    uint16_t len;
    uint8_t  uid[256 - sizeof(uint16_t)];
} sm2_userid_t;

typedef struct csv_sm2_signature_in_cert {
    uint8_t r[72];
    uint8_t s[72];
    /* we don't care the reserved size */
    uint8_t reserved[1];
} csv_signature_t;

typedef struct _sm2_signature {
    uint8_t r[32];
    uint8_t s[32];
} sm2_signature_t;

int read_priv_key_pem_into_evpkey(const char *file_name, EVP_PKEY** evp_priv_key);

CSV_ERROR_CODE convert_pubkey_to_cert(csv_cert* cert, EVP_PKEY* evp_pubkey);

int sign_with_key_in_sig1(csv_cert *m_child_cert, uint32_t version, uint32_t pub_key_usage,
                          uint32_t pub_key_algo, EVP_PKEY** priv_evp_key,
                          uint32_t sig_1_usage, uint32_t sig_1_algo, const char* userid);

int sign_with_key_in_sig2(csv_cert *m_child_cert, uint32_t version, uint32_t pub_key_usage,
                          uint32_t pub_key_algo, EVP_PKEY** priv_evp_key,
                          uint32_t sig_2_usage, uint32_t sig_2_algo, const char* userid);

csv_cert* create_oca_cert(EVP_PKEY** oca_key_pair, uint8_t api_major, uint8_t api_minor, const char *userid);

#endif /* CSVCERT_H */